﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using System.Drawing;

namespace Utils
{
    public class UploadResult
    {
        public int NewID { get; set; }
        public UploadResultEnum Result { get; set; }
    }

    public enum UploadResultEnum
    { 
        Success = 1,
        InternalError = -1,
        FileSizeTooBig = -2,
        ImageWidthInvalid = -3,
        ImageHeightInvalid = -4,
        ImageTypeInvalid = -5,
        NoFileSelected = -6
    }

    public enum AvatarSizeEnum
    { 
        Avatar_80x80,
        Avatar_48x48,
        Avatar_32x32,
        Avatar_16x16
    }

    public enum ShotSizeEnum
    { 
        Shot_Org, //_Org means orginal file
        Shot_900x600,
        Shot_600x400,
        Shot_450x300,
        Shot_300x200,
        Shot_200x133,
        Shot_90x60
    }

    public class ImageCroppingSizeVersion
    {
        public int Width { get; set; }
        public int Height { get; set; }
        public Boolean IsOrginal{get;set;}
    }

    public enum AvatarType
    { 
        IndAvatar = 1,
        CoAvatar = 2,
    }

    public class ImageUtils
    {
        private static string _fileNameOrgTemplate = "{0}-org.{1}";
        private static string _fileNameTemplate = "{0}-{1}x{2}.{3}"; //0= shotID or "Avatar", {1} = width, {2} = height, {3} = file extension

        private static List<ImageCroppingSizeVersion> GetAllImageCroppingSizeVersions<T>()
        {
            var result = new List<ImageCroppingSizeVersion>();
            foreach (T size in CommonUtilities.GetEnumValues<T>())
            {
                result.Add(GetImageCroppingSizeVersion(size));
            }
            return result;
        }

        /// <summary>
        /// convert AvatarSizeEnum/ShotSizeEnum to ImageCroppingSizeVersion
        /// </summary>
        /// <param name="availableImageSizeEnum"></param>
        /// <returns></returns>
        private static ImageCroppingSizeVersion GetImageCroppingSizeVersion(dynamic availableImageSizeEnum)
        {
            string value = availableImageSizeEnum.ToString();
            var version = new ImageCroppingSizeVersion();

            if (!value.ToLower().EndsWith("_org"))
            {
                string[] values = value.Substring(value.IndexOf("_") + 1).Split('x');
                version.Width = Convert.ToInt32(values[0]);
                version.Height = Convert.ToInt32(values[1]);
                version.IsOrginal = false;
            }
            else
            {
                version.IsOrginal = true;
            }
            return version;
        }

        /// <summary>
        /// Image Folder Structure design: 
        /// 
        /// /ind/{id}/avatar/avatar-80x80.jpg or avatar-48x48.jpg, store file extension, since we support jpg, png, gif
        /// /ind/{id}/{shotID}.jpg, {shotID-300x200}, 150x100, 90x60
        /// /co/{id}/avatar/avatar_80x80.jpg ..
        /// 
        /// </summary>
        /// <param name="imageType"></param>
        /// <param name="indID"></param>
        /// <returns></returns>
        private static string GetShotImageBasePath(string webProjectPhysicalPath, int indID, Boolean createDir = false)
        {
            string level1Path = webProjectPhysicalPath + "/images/ind/{0}/";
            level1Path = string.Format(level1Path, indID);
            if (createDir)
            {
                if (!Directory.Exists(level1Path))
                {
                    try
                    {
                        Directory.CreateDirectory(level1Path);
                    }
                    catch { }
                }
            }
            return level1Path;
        }

        /// <summary>
        /// only when uploading image, createDir = true, 
        /// </summary>
        /// <param name="webProjectPhysicalPath"></param>
        /// <param name="avatarType"></param>
        /// <param name="id"></param>
        /// <param name="createDir"></param>
        /// <returns></returns>
        private static string GetAvatarBasePath(string webProjectPhysicalPath, AvatarType avatarType, int id, Boolean createDir = false)
        {
            string level1Path = webProjectPhysicalPath + "/images/{0}/{1}/";
            string level2Path = level1Path + "avatar/";

            string folder = string.Empty;
            if (avatarType == AvatarType.IndAvatar)
            {
                folder = "ind";
            }
            else if (avatarType == AvatarType.CoAvatar)
            {
                folder = "co";
            }
            else 
            {
                throw new Exception("error");
            }

            level1Path = string.Format(level1Path, folder, id);
            level2Path = string.Format(level2Path, folder, id);
            if (createDir)
            {
                if (!Directory.Exists(level1Path))
                {
                    try
                    {
                        Directory.CreateDirectory(level1Path);
                    }
                    catch
                    {}
                }
                if (!Directory.Exists(level2Path))
                {
                    try
                    {
                        Directory.CreateDirectory(level2Path);
                    }
                    catch
                    {}
                }
            }
            
            return level2Path;
        }

        /// <summary>
        /// full physical path for shot image (when uploading, use it)
        /// </summary>
        /// <param name="webProjectPhysicalPath"></param>
        /// <param name="indID"></param>
        /// <param name="shotID"></param>
        /// <param name="shotSizeEnum"></param>
        /// <param name="fileExtension"></param>
        /// <returns></returns>
        private static string GetShotImageFullPhysicalPath(string webProjectPhysicalPath, int indID, int shotID, ShotSizeEnum shotSizeEnum, string fileExtension)
        {
            string destBasePath = GetShotImageBasePath(webProjectPhysicalPath, indID, true); //true means guarantee creating dir
            ImageCroppingSizeVersion version = GetImageCroppingSizeVersion(shotSizeEnum);
            string finalPath = destBasePath + string.Format(_fileNameTemplate, shotID, version.Width, version.Height, fileExtension);
            if (version.IsOrginal)
            {
                finalPath = destBasePath + string.Format(_fileNameOrgTemplate, shotID, fileExtension);
            }
            return finalPath;
        }

        /// <summary>
        /// full physical path for avatar image (when uploading, use it)
        /// </summary>
        /// <param name="webProjectPhysicalPath"></param>
        /// <param name="avatarType"></param>
        /// <param name="id"></param>
        /// <param name="avatarSizeEnum"></param>
        /// <param name="fileExtension"></param>
        /// <returns></returns>
        private static string GetAvatarFullPhsicalPath(string webProjectPhysicalPath, AvatarType avatarType, int id, AvatarSizeEnum avatarSizeEnum, string fileExtension)
        {
            string destBasePath = GetAvatarBasePath(webProjectPhysicalPath, avatarType, id, true); //true means guarantee creating dir
            ImageCroppingSizeVersion version = GetImageCroppingSizeVersion(avatarSizeEnum);
            string finalPath = destBasePath + string.Format(_fileNameTemplate, "Avatar", version.Width, version.Height, fileExtension);
            if (version.IsOrginal)
            {
                finalPath = destBasePath + string.Format(_fileNameOrgTemplate, "Avatar", fileExtension);
            }
            return finalPath;
        }

        public static string GetShotRelativeUrl(int indID, int shotID, ShotSizeEnum shotSizeEnum, string fileExtension)
        {
            string destBasePath = GetShotImageBasePath(string.Empty, indID, false);
            ImageCroppingSizeVersion version = GetImageCroppingSizeVersion(shotSizeEnum);
            string finalPath = destBasePath + string.Format(_fileNameTemplate, shotID, version.Width, version.Height, fileExtension);
            if (version.IsOrginal)
            {
                finalPath = destBasePath + string.Format(_fileNameOrgTemplate, shotID, fileExtension);
            }
            return finalPath;
        }

        public static string GetAvatarRelativeUrl(AvatarType avatarType, int id, AvatarSizeEnum avatarSizeEnum, string fileExtension)
        {
            if (string.IsNullOrEmpty(fileExtension))
            {
                
                return GetAvatarDefaultUrl(avatarType, avatarSizeEnum);
            }
            else
            {
                string destBasePath = GetAvatarBasePath(string.Empty, avatarType, id, false); //true means guarantee creating dir
                ImageCroppingSizeVersion version = GetImageCroppingSizeVersion(avatarSizeEnum);
                string finalPath = destBasePath + string.Format(_fileNameTemplate, "Avatar", version.Width, version.Height, fileExtension);
                if (version.IsOrginal)
                {
                    finalPath = destBasePath + string.Format(_fileNameOrgTemplate, "Avatar", fileExtension);
                }
                return finalPath;
            }
        }

        public static string GetAvatarDefaultUrl(AvatarType avatarType, AvatarSizeEnum avatarSizeEnum)
        {
            string pathTemplate = "/content/images/{0}_default_{1}.png";
            int size = 80;
            if (avatarSizeEnum == AvatarSizeEnum.Avatar_16x16)
            {
                size = 16;
            }
            else if (avatarSizeEnum == AvatarSizeEnum.Avatar_32x32)
            {
                size = 32;
            }
            else if (avatarSizeEnum == AvatarSizeEnum.Avatar_48x48)
            {
                size = 48;
            }
            else if (avatarSizeEnum == AvatarSizeEnum.Avatar_80x80)
            {
                size = 80;
            }
            string accountType = avatarType == AvatarType.IndAvatar ? "ind" : "co";
            return string.Format(pathTemplate, accountType, size);
        }

        public static string GetImageFileExtension(HttpPostedFileBase file)
        {
            string fileName = file.FileName;
            string fileExtension = fileName.Substring(fileName.LastIndexOf(".") + 1);
            return fileExtension;
        }

        /// newShotID = success,
        /// -1 = internal error
        /// -2 = file size too big
        /// -3 = width size problem
        /// -4 = height size problem
        /// -5 = not valid image file
        /// -6 = no file has been selected
        public static UploadResult UploadShot(HttpPostedFileBase file, string webProjectPhysicalPath, int indID, string title, string description)
        {
            int maxFileSize = 1024 * 1024; // 1M
            int validWidth = 900;
            int validHeight = 600;

            if (file == null)
            {
                return new UploadResult { Result = UploadResultEnum.NoFileSelected };
            }
            if (file.ContentLength > maxFileSize)
            {
                return new UploadResult { Result = UploadResultEnum.FileSizeTooBig };
            }
            string contentType = file.ContentType.ToLower();
            if (contentType != "image/jpeg" &&
                contentType != "image/pjpeg" &&
                contentType != "image/jpg" &&
                contentType != "image/png" &&
                contentType != "image/x-png" &&
                contentType != "image/gif")
            {
                return new UploadResult { Result = UploadResultEnum.ImageTypeInvalid };
            }

            try
            {
                //now create image obj, then check width, height
                Image image = Image.FromStream(file.InputStream, true, true);

                if (image.Width < validWidth)
                {
                    image.Dispose();
                    image = null;
                    return new UploadResult { Result = UploadResultEnum.ImageWidthInvalid };
                }
                if (image.Height < validHeight)
                {
                    image.Dispose();
                    image = null;
                    return new UploadResult { Result = UploadResultEnum.ImageHeightInvalid };
                }
                //pass all checks, now, try to create a new record in shot table, and then upload image, generate different versions
                string fileName = file.FileName;
                string fileExtension = GetImageFileExtension(file);
                string orginalFileName = fileName;
                int index = fileName.LastIndexOf('\\');
                if (index >= 0)
                {
                    orginalFileName = fileName.Substring(index + 1);
                }
                else
                {
                    index = fileName.LastIndexOf('/');
                    if (index >= 0)
                    {
                        orginalFileName = fileName.Substring(index + 1);
                    }
                }
                int newShotID = DataUtils.InsertShot(indID, title, description, image.Width, image.Height, file.ContentLength, fileExtension, orginalFileName);
                if (newShotID > 0)
                {
                    //save cropping versions
                    foreach (var size in CommonUtilities.GetEnumValues<ShotSizeEnum>())
                    {
                        var version = GetImageCroppingSizeVersion(size);
                        string destFullFileName = GetShotImageFullPhysicalPath(webProjectPhysicalPath, indID, newShotID, size, fileExtension);
                        if (version.IsOrginal)
                        {
                            file.SaveAs(destFullFileName);
                        }
                        else
                        {
                            CroppingHelper.Build(image, destFullFileName, version.Width, version.Height, -1);
                        }
                    }

                    //destroy image
                    image.Dispose();
                    image = null;
                    return new UploadResult { Result = UploadResultEnum.Success, NewID = newShotID };
                }
                else
                {
                    //destroy image
                    image.Dispose();
                    image = null;
                    return new UploadResult { Result = UploadResultEnum.InternalError };
                }
            }
            catch
            {
                return new UploadResult { Result = UploadResultEnum.InternalError };
            }
        }


        /// 1 = success,
        /// -1 = internal error
        /// -2 = file size too big
        /// -3 = width size problem
        /// -4 = height size problem
        /// -5 = not valid image file
        /// -6 = no file has been selected
        public static UploadResult UploadAvatar(HttpPostedFileBase file, string webProjectPhysicalPath, AvatarType avatarType, int id)
        {
            int maxFileSize = 700 * 1024; // 700k
            if (file == null)
            {
                return new UploadResult { Result = UploadResultEnum.NoFileSelected };
            }
            if (file.ContentLength > maxFileSize)
            {
                return new UploadResult { Result = UploadResultEnum.FileSizeTooBig };
            }
            string contentType = file.ContentType.ToLower();
            if (contentType != "image/jpeg" &&
                contentType != "image/pjpeg" &&
                contentType != "image/jpg" &&
                contentType != "image/png" &&
                contentType != "image/x-png" &&
                contentType != "image/gif")
            {
                return new UploadResult { Result = UploadResultEnum.ImageTypeInvalid };
            }

            try
            {
                //pass all checks, now, update ind/co avatar flag 
                string fileName = file.FileName;
                string fileExtension = fileName.Substring(fileName.LastIndexOf(".") + 1);

                int result = DataUtils.UpdateAvatar(avatarType, id, fileExtension);

                if (result > 0)
                {
                    //now create image obj, then check width, height
                    Image image = Image.FromStream(file.InputStream, true, true);

                    //save cropping versions
                    foreach (var size in CommonUtilities.GetEnumValues<AvatarSizeEnum>())
                    {
                        var version = GetImageCroppingSizeVersion(size);
                        string destFullFileName = GetAvatarFullPhsicalPath(webProjectPhysicalPath, avatarType, id, size, fileExtension);
                        if (version.IsOrginal)
                        {
                            file.SaveAs(destFullFileName);
                        }
                        else
                        {
                            CroppingHelper.Build(image, destFullFileName, version.Width, version.Height, -1);
                        }
                    }
                    //destroy image
                    image.Dispose();
                    image = null;
                    return new UploadResult { Result = UploadResultEnum.Success, NewID = 1 };
                }
                else
                {
                    return new UploadResult { Result = UploadResultEnum.InternalError };
                }

            }
            catch
            {
                return new UploadResult { Result = UploadResultEnum.InternalError };
            }
        }
    }
}
